-module(generic_algo).

-compile(export_all).

test() ->
    Ind = individual(5,0,100),
    Popu = population(10,5,0,100),
    fitness(Ind,300),
    grade(Popu, 304).

test1(Age) ->
    Target = 371,
    Popu = population(100,5,0,100),
    {_,History} = evolve(Popu, Target, Age, []),
    History.

individual(Length, Min, Max) ->
    [generic_algo_lib:rand(Min,Max) || _N<-lists:seq(1,Length)].

population(Count, Length, Min, Max) ->
    [individual(Length,Min,Max) || _N<-lists:seq(1,Count)].

fitness(Individual, Target) ->
    Sum = lists:sum(Individual),
    abs(Target-Sum).

grade(Popu, Target) ->
    %%'Find average fitness for a population.'
    Summed = lists:sum( [ fitness(Ind, Target) || Ind<-Popu ] ),
    Summed /length(Popu).

evolve(Popu, _Target, 0) ->
    Popu;
evolve(Popu, Target, N) ->
    NPopu = evolve(Popu, Target, 0.2, 0.05, 0.01),
    evolve(NPopu, Target, N-1).

evolve(Popu, Target, 0, History) ->
    H = [grade(Popu,Target)|History],
    {Popu,lists:reverse(H)};
evolve(Popu, Target, N, History) ->
    NPopu = evolve(Popu, Target, 0.2, 0.05, 0.01),
    evolve(NPopu, Target, N-1, [grade(Popu, Target)|History]).

%%evolve(Pop, Target, Retain=0.2, random_select=0.05, mutate(突变)=0.01)
evolve(Popu, Target, Retain, Random_select, Mutate) ->
    Graded0 = [{fitness(X, Target), X} || X<-Popu],
    Graded = [ element(2,X) || X<-lists:keysort(1,Graded0)],%X是一个{fitness,individual}
    Retain_length = round(length(Graded)*Retain),%%每次遴选的长度
    {Parents,Rest} = lists:split(Retain_length,Graded),%%遴选出的做繁殖者，遴选的越少杂交的越多

    %% randomly add other individuals to
    %% promote genetic diversity
    Parents1 = lists:foldl( fun(Ind,Acc) -> %%随机选出一些不满足条件的
				    Rdm = random:uniform(),
				    if
					Random_select > Rdm ->
					    [Ind|Acc];
					true ->
					    Acc
				    end
			    end, Parents, Rest),

    Parents2 = lists:map( fun(Ind) -> %%进行变异
				  Rdm = random:uniform(),
				  if
				      Mutate > Rdm ->
					  Pos_to_mutate = generic_algo_lib:rand(1, length(Ind)),
					  NewValue = generic_algo_lib:rand(lists:min(Ind), lists:max(Ind)),
					  %%NewValue = generic_algo_lib:rand(0, lists:max(Ind)),
					  generic_algo_lib:list_set_value(Ind, Pos_to_mutate, NewValue);
				      true ->
					  Ind
				  end
			  end, Parents1),
    %% crossover parents to create children
    crossover(Parents2,Popu).

crossover(Parents, Popu) ->
    crossover(Parents, Popu, length(Popu)-length(Parents)).

%%杂交
crossover(Parents, _Popu, 0) ->
    Parents;
crossover(Parents, Popu, Desired_length) ->
    Male = generic_algo_lib:rand(1, length(Parents)),
    Female = generic_algo_lib:rand(1, length(Parents)),
    if
	Male == Female ->
	    crossover(Parents, Popu, Desired_length);
	true ->
	    Males = lists:nth(Male, Parents),
	    Females = lists:nth(Female,Parents),
	    Half = round(length(Males) / 2),
	    Child = lists:sublist(Males,Half) ++ lists:nthtail(Half,Females),
	    crossover([Child|Parents], Popu, Desired_length-1)
    end.

